2024.4.15 インデックス計算
2023.4.20 修正
修正するべし
指数加重平均のところに間違いあり
evmの計算を取り除く。Index=の式でOK => 修正済み
Lambdaの設定方法が不適切
最適化手法を用いる
=> 全然機能しなかった 0.9999になる。
=> 手動で設定
window期間を決める方法が不適切
目的は、取引が少ないエリアのwindow期間を伸ばし、多いエリアを短くしたいだけ。
直近の取引量の正規化で決めていた❌
そのエリアが30日間に平均何回取引があるかを基準にする
=> サンプル数によって新しいエリアは不利になるのでは
=> エリアの取引が初めて起きた日~最新の取引日の期間にする?
? 全体から相対的に決めるべきではない?1日に1回取引があるエリアなら十分と言える?
ウィンドウは7日~30日 rollingした後何取引分のデータか
手動で基準設定
table: window
window 最低取引数 / 月 1日あたり
30 20 0.666
29 24 0.827
28 28 1
27 32 1.185
26 36 1.384
25 40 1.6
24 44 1.83
23 48 2.08
22 52 2.363
21 56 2.66
20 60 3
19 64 3.368
18 68 3.777
17 72 4.235
16 76 4.75
15 80 5.333
14 84 6
13 88 6.769
12 92 7.666
11 96 8.727
10 100 10
9 104 11.555
8 108 13.5
7 112 16
code: python
import pandas as pd
from datetime import timedelta
import statsmodels.api as sm
from sklearn.linear_model import LinearRegression
from sklearn.feature_selection import SelectKBest, f_regression
from sklearn.feature_selection import RFE
import numpy as np
import matplotlib.pyplot as plt
import datetime
pd.set_option('display.max_columns', None)
データ整える
code: python
df_sale = pd.read_csv('Transactions.csv')
import warnings
warnings.filterwarnings('ignore')
df_sale = df_sale[df_sale'property_type_en' != 'Land']
df_sale = df_sale[df_sale'property_type_en' == 'Unit']
df_sale = df_sale[df_sale'property_usage_en' == 'Residential']
df_sale = df_sale'instance_date', 'area_name_en', 'building_name_en', 'master_project_en', "meter_sale_price"
df_sale = df_sale.rename(columns={'instance_date': 'date'})
df_sale = df_sale.rename(columns={'area_name_en': 'area'})
df_sale = df_sale.rename(columns={'building_name_en': 'building'})
df_sale = df_sale.rename(columns={'master_project_en': 'project'})
df_sale = df_sale.rename(columns={'instance_date': 'date'})
df_sale = df_sale.rename(columns={'area_name_en': 'area'})
df_sale = df_sale.rename(columns={'building_name_en': 'building'})
df_sale = df_sale.rename(columns={'master_project_en': 'project'})
全体のエリアごとの変数を求める
code: python
#パーセンタイルで抽出
p_below = df_sale'meter_sale_price'.quantile(0.35)
p_upper = df_sale'meter_sale_price'.quantile(0.65)
percentile_df_sale = df_sale[(df_sale'meter_sale_price' >= p_below) & (df_sale'meter_sale_price' <= p_upper)]
# 過去90日前の日付を計算
#ここのend_dateが算出したい日
end_date = dateti
me.datetime(2022,12,1)
#end_date = percentile_df_sale.index.max()
start_date = end_date - datetime.timedelta(days=90)
# 過去90日間のデータをフィルタリング
filtered_df = percentile_df_sale(percentile_df_sale.index >= start_date) & (percentile_df_sale.index <= end_date)
# 上記の期間におけるエリアごとに取引量を集計
transaction_count = filtered_df.groupby('area').size()
#動的ウィンドウ定義
max_window = 30
min_window = 7
# エリアごとの取引量を正規化(0から1の範囲にする)
normalized_count = (transaction_count - transaction_count.min()) / (transaction_count.max() - transaction_count.min())
# エリアごとのウィンドウ期間を決定する
dynamic_window = max_window - (max_window - min_window) * normalized_count
dynamic_window = np.round(dynamic_window).astype(int)
# dynamic_windowの値をカラムにしてデータフレームを作成
df_window = pd.DataFrame({'dynamic_window': dynamic_window})
# エリアごとの減衰係数 lamda を取引量の正規化の値で定義する
# ここ直す
lambdas = normalized_count
df_lambda = pd.DataFrame({'lambda': lambdas})
#動的ウィンドウ期間と減退係数の決定が完了
エリアごとのインデックスを求める
code: python
select_area = 'Marsa Dubai'
df_spe_area = df_sale[df_sale'area' == select_area]
#算出日以前のデータフレームに変換
specified_date = end_date
df_spe_area = df_spe_areadf_spe_area.index <= specified_date
# 同じ日付のインデックスでグループ化し、meter_sale_priceの中央値を計算
area_df_median = df_spe_area.groupby(df_spe_area.index)'meter_sale_price'.median()
# 中央値を新しいデータフレームに変換
area_df_median = area_df_median.to_frame()
# area_df_medianデータフレームのインデックスをリセット
area_df_median.reset_index(inplace=True)
area_df_median.set_index('date', inplace=True)
area_df_median.index = pd.to_datetime(area_df_median.index)
area_df_median = area_df_median.sort_index()
# 新しいデータフレームのインデックスにおいて、日付を連続するように補完し、空欄の行は直前のデータを参照する。
area_df_median = area_df_median.asfreq('D', method='ffill')
# カラム meter_sale_price を線形補完
area_df_median'meter_sale_price'.interpolate(method='linear', inplace=True)
# ローリングウィンドウを適用して smoothed_price を計算
window = df_window.locselect_area, 'dynamic_window'
area_df_median'smoothed_price' = area_df_median'meter_sale_price'.rolling(window=window, min_periods=1).median()
# 動的ウィンドウを使って移動中央値を算出完了
減退係数を使って指数加重平均化してから7日間で平滑化
code:python
#ここも直す
lambda_ = df_lambda.locselect_area, 'lambda'
# 重みを計算
W_ti = 1 / lambda_
W_si = 1 - W_ti
# end_dateの1日前を取得
end_date_minus_one = end_date - datetime.timedelta(days=1)
# end_date以前の最新のデータを取得
latest_data_before_end_date = area_df_median.locarea_df_median.index <= end_date_minus_one.iloc-1
p_si = latest_data_before_end_date'smoothed_price'
# 最新のタイムリーデータを取得 #ここどっちのデータ使う? smoothed?
pt_i = area_df_median'meter_sale_price'.iloc-1
# 最終的なインデックスを算出
area_df_median'original_index' = (p_si * W_si) + (pt_i * W_ti)
# 直近7日間でのrolling
rolling_window = 7
area_df_median'property_index' = area_df_median'original_index'.rolling(window=rolling_window).median()
求めたIndexをデータフレームに格納
code: python
latest_date = area_df_median.index.max()
latest_row = area_df_median.loc[area_df_median.index == latest_date, 'property_index']
latest_row'date' = latest_date
latest_row = latest_row.set_index('date')
if 'new_df' not in locals():
new_df = latest_row
else:
new_df = pd.concat(new_df, latest_row, axis=0)
new_df = new_df.sort_index()
データをグラフにする
code: python
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
# 表示する日付の範囲を指定
start_date = '2022-12-01'
end_date = '2023-02-20'
# グラフの大きさを設定
fig, ax = plt.subplots(figsize=(16, 8))
# データをプロット
ax.plot(new_df.index, new_df'property_index', linewidth=1, color='royalblue')
# グラフのx軸の範囲を設定
ax.set_xlim(pd.to_datetime(start_date), pd.to_datetime(end_date))
# グラフのタイトルとラベルを設定
ax.set_title('Property_index')
ax.set_xlabel('Date')
ax.set_ylabel('Index')
# グラフを表示
plt.show()